/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file power_manager.c
 * @brief 电源屏幕状态管理模块
 */

#include "nci_module.h"
#include "nfa_rw.h"
#include "nfc_osi_api.h"
#include "nfc_log_api.h"
#include "nci_core.h"

#define NCI_SCREEN_STATE_MASK       0x0F

static tMUTEX s_pm_lock = MUTEX_INIT_VAL("pm");
static uint8_t s_prev_screen_state = NCI_SCREEN_STATE_UNKNOWN;

static tNCI_STATUS pm_init(const tNCI_MODULE_INIT_CFG *p_cfg) {
    (void) p_cfg;
    tNFC_STATUS ret = osi_mutex_init(&s_pm_lock);
    if (ret != NFC_STATUS_OK) {
        NFCLOG_E("pm lock init failed, ret = %d", ret);
        return NCI_STATUS_EXT_FAILED;
    }
    if (osi_mutex_lock(&s_pm_lock) != NFC_STATUS_OK) {
        NFCLOG_E("pm lock failed");
        osi_mutex_deinit(&s_pm_lock);
        return NCI_STATUS_EXT_GET_LOCK_FAILED;
    }
    s_prev_screen_state = NCI_SCREEN_STATE_OFF_LOCKED;
    osi_mutex_unlock(&s_pm_lock);
    return NCI_STATUS_OK;
}

static tNCI_STATUS pm_deinit(bool is_shutdown) {
    (void) is_shutdown;
    if (osi_mutex_lock(&s_pm_lock) != NFC_STATUS_OK) {
        NFCLOG_E("pm lock failed");
        return NCI_STATUS_EXT_GET_LOCK_FAILED;
    }
    s_prev_screen_state = NCI_SCREEN_STATE_UNKNOWN;
    osi_mutex_unlock(&s_pm_lock);
    tNFC_STATUS ret = osi_mutex_deinit(&s_pm_lock);
    if (ret != NFC_STATUS_OK) {
        NFCLOG_E("pm lock deinit failed, ret = %d", ret);
    }
    return NCI_STATUS_OK;
}

static void pm_dump(int fd) {
    osi_dump(fd, "screen_state=0x%02X", s_prev_screen_state);
}

tNCI_MODULE g_power_manager = {
    .name = "Power Manager",
    .state = NCI_MODULE_STATE_DISABLED,
    .init = pm_init,
    .deinit = pm_deinit,
    .dump = pm_dump,
};

static tNCI_STATUS set_power_sub_state_for_screen_state(uint8_t screen_state) {
    tNCI_POWER_SUB_STATE power_sub_state = NCI_POWER_SUB_STATE_INVALID;
    switch (screen_state) {
        case NCI_SCREEN_STATE_ON_LOCKED:
            power_sub_state = NCI_POWER_SUB_STATE_ON_LOCKED;
            break;
        case NCI_SCREEN_STATE_ON_UNLOCKED:
            power_sub_state = NCI_POWER_SUB_STATE_ON_UNLOCKED;
            break;
        case NCI_SCREEN_STATE_OFF_UNLOCKED:
            power_sub_state = NCI_POWER_SUB_STATE_OFF_UNLOCKED;
            break;
        case NCI_SCREEN_STATE_OFF_LOCKED:
            power_sub_state = NCI_POWER_SUB_STATE_OFF_LOCKED;
            break;
        default:
            NFCLOG_E("invalid screen state: %d", screen_state);
            return NCI_STATUS_EXT_FAILED;
    }
    return nci_core_set_power_sub_state(power_sub_state);
}

/**
 * @brief 设置屏幕状态
 *
 * @param screen_state_mask 屏幕状态掩码
 * @param always_poll 是否始终打开Polling
 *
 * @return 成功返回NCI_STATUS_OK，失败返回其他错误码
 */
tNCI_STATUS nfa_set_screen_state(uint8_t screen_state_mask, bool always_poll) {
    NFCLOG_D("screen_state=0x%02X, always_poll=%d", screen_state_mask, always_poll);
    if (osi_mutex_lock(&s_pm_lock) != NFC_STATUS_OK) {
        return NCI_STATUS_EXT_GET_LOCK_FAILED;
    }
    tNCI_STATUS ret = NCI_STATUS_EXT_FAILED;
    uint8_t screen_state = (screen_state_mask & NCI_SCREEN_STATE_MASK);
    if (s_prev_screen_state == screen_state) {
        NFCLOG_I("screen state is not changed, no need to change");
        ret = NCI_STATUS_OK;
        goto unlock;
    }

    switch (s_prev_screen_state) {
        case NCI_SCREEN_STATE_OFF_LOCKED:
        // [[fallthrough]];
        case NCI_SCREEN_STATE_OFF_UNLOCKED:
        // [[fallthrough]];
        case NCI_SCREEN_STATE_ON_LOCKED:
            ret = set_power_sub_state_for_screen_state(screen_state);
            if (ret != NCI_STATUS_OK) {
                NFCLOG_E("set power sub state failed, ret = %d", ret);
                goto unlock;
            }
            break;
    }

    uint8_t discovery_param = NCI_POLLING_DH_ENABLE_MASK | NCI_LISTEN_DH_NFCEE_ENABLE_MASK;
    switch (screen_state) {
        case NCI_SCREEN_STATE_OFF_LOCKED:
        // [[fallthrough]];
        case NCI_SCREEN_STATE_OFF_UNLOCKED:
            discovery_param = NCI_POLLING_DH_DISABLE_MASK | NCI_LISTEN_DH_NFCEE_ENABLE_MASK;
            break;
        case NCI_SCREEN_STATE_ON_LOCKED:
            discovery_param = (screen_state_mask & NCI_SCREEN_POLLING_TAG_MASK) ?
                                (NCI_POLLING_DH_ENABLE_MASK | NCI_LISTEN_DH_NFCEE_ENABLE_MASK) :
                                (NCI_POLLING_DH_DISABLE_MASK | NCI_LISTEN_DH_NFCEE_ENABLE_MASK);
            break;
        case NCI_SCREEN_STATE_ON_UNLOCKED:
            discovery_param = NCI_POLLING_DH_ENABLE_MASK | NCI_LISTEN_DH_NFCEE_ENABLE_MASK;
            break;
    }

    if (!always_poll) {
        tNCI_REG reg = {.id = NCI_REG_CON_DISCOVERY_PARAM, .len = 1, .value = &discovery_param};
        ret = nci_core_set_config(&reg, 1);
        if (ret != NCI_STATUS_OK) {
            NFCLOG_E("set discovery param failed, ret = %d", ret);
            goto unlock;
        }
    }
    if (s_prev_screen_state == NCI_SCREEN_STATE_ON_UNLOCKED) {
        ret = set_power_sub_state_for_screen_state(screen_state);
        if (ret != NCI_STATUS_OK) {
            NFCLOG_E("set power sub state failed, ret = %d", ret);
        }
    }

    if ((screen_state == NCI_SCREEN_STATE_OFF_LOCKED || screen_state == NCI_SCREEN_STATE_OFF_UNLOCKED) &&
        (s_prev_screen_state == NCI_SCREEN_STATE_ON_UNLOCKED || s_prev_screen_state == NCI_SCREEN_STATE_ON_LOCKED)) {
        // 灭屏时断开读卡连接
        nfa_rw_disconnect();
    }

unlock:
    s_prev_screen_state = screen_state;
    osi_mutex_unlock(&s_pm_lock);
    return ret;
}
